﻿using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Net;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using CaptchaControl.Model;

namespace CaptchaControl.Control
{
    public partial class CaptchaControl : UserControl
    {        
        private readonly DependencyProperty quantityCharactersProperty;
        public string QuantityCharacters
        {
            get { return (string)GetValue(quantityCharactersProperty); }
            set { SetValue(quantityCharactersProperty, value); }
        }

        private readonly DependencyProperty backgroundColorStartProperty;
        public Color BackgroundColorStart
        {
            get { return (Color)GetValue(backgroundColorStartProperty); }
            set { SetValue(backgroundColorStartProperty, value); }
        }

        private readonly DependencyProperty backgroundColorEndProperty;
        public Color BackgroundColorEnd
        {
            get { return (Color)GetValue(backgroundColorEndProperty); }
            set { SetValue(backgroundColorEndProperty, value); }
        }

        private readonly DependencyProperty foregroundColorStartProperty;
        public Color ForegroundColorStart
        {
            get { return (Color)GetValue(foregroundColorStartProperty); }
            set { SetValue(foregroundColorStartProperty, value); }
        }

        private readonly DependencyProperty foregroundColorEndProperty;
        public Color ForegroundColorEnd
        {
            get { return (Color)GetValue(foregroundColorEndProperty); }
            set { SetValue(foregroundColorEndProperty, value); }
        }

        private readonly DependencyProperty spaceBetweenLettersProperty;
        public int SpaceBetweenLetters
        {
            get { return (int)GetValue(spaceBetweenLettersProperty); }
            set { SetValue(spaceBetweenLettersProperty, value); }
        }

        private readonly DependencyProperty isUniqueForegroundColor;
        public bool IsUniqueForegroundColor
        {
            get { return (bool)GetValue(isUniqueForegroundColor); }
            set { SetValue(isUniqueForegroundColor, value); }
        }

        private readonly DependencyProperty isUniqueBackgroundColor;
        public bool IsUniqueBackgroundColor
        {
            get { return (bool)GetValue(isUniqueBackgroundColor); }
            set { SetValue(isUniqueBackgroundColor, value); }
        }

        private readonly DependencyProperty captchaTextProperty;
        public string CaptchaText
        {
            get { return (string)GetValue(captchaTextProperty); }
            private set { SetValue(captchaTextProperty, value); }
        }

        #region Global Variables

        private readonly Random random;
        private readonly Color color = new Color();
        private static readonly char[] CharArray = "ABCDEFGHIJKLMNOPQRSTUVWXYZ123467890".ToCharArray();
        #endregion

        public ObservableCollection<Captcha> CaptchaLetters { get; set; }

        public CaptchaControl()
        {
            random = new Random();
            InitializeComponent();
            quantityCharactersProperty = DependencyProperty.Register("QuantityCharacters",
                                                                     typeof(string),
                                                                     typeof(CaptchaControl),
                                                                     new PropertyMetadata(OnQuantityCharactersChanged));
            backgroundColorStartProperty = DependencyProperty.Register("BackgroundColorStart",
                                                                     typeof(Color),
                                                                     typeof(CaptchaControl),
                                                                     new PropertyMetadata(OnColorStartChanged));
            backgroundColorEndProperty = DependencyProperty.Register("BackgroundColorEnd",
                                                                     typeof(Color),
                                                                     typeof(CaptchaControl),
                                                                     new PropertyMetadata(OnColorEndChanged));
            spaceBetweenLettersProperty = DependencyProperty.Register("SpaceBetweenLetters",
                                                                     typeof(int),
                                                                     typeof(CaptchaControl),
                                                                     new PropertyMetadata(OnSpaceBetweenLettersChanged));
            foregroundColorStartProperty = DependencyProperty.Register("ForegroundColorStart",
                                                                     typeof(Color),
                                                                     typeof(CaptchaControl),
                                                                     new PropertyMetadata(OnForeColorStartChanged));
            foregroundColorEndProperty = DependencyProperty.Register("ForegroundColorEnd",
                                                                     typeof(Color),
                                                                     typeof(CaptchaControl),
                                                                     new PropertyMetadata(OnForeColorEndChanged));
            isUniqueForegroundColor = DependencyProperty.Register("IsUniqueForegroundColor",
                                                                     typeof(bool),
                                                                     typeof(CaptchaControl),
                                                                     new PropertyMetadata(OnIsUniqueForegroundColorChanged));
            captchaTextProperty = DependencyProperty.Register("CaptchaText",
                                                                     typeof(string),
                                                                     typeof(CaptchaControl),
                                                                     null);
            isUniqueBackgroundColor = DependencyProperty.Register("IsUniqueBackgroundColor",
                                                                     typeof(bool),
                                                                     typeof(CaptchaControl),
                                                                     new PropertyMetadata(OnIsUniqueBackgroundColorChanged));
        }

        private void OnIsUniqueBackgroundColorChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            this.SetBackgroundGradient();
        }

        private void OnSpaceBetweenLettersChanged(object sender, DependencyPropertyChangedEventArgs e)
        {
            foreach (var captcha in CaptchaLetters)
            {
                captcha.SpaceBetweenLetters = SpaceBetweenLetters;
            }
        }

        private void OnIsUniqueForegroundColorChanged(object sender, DependencyPropertyChangedEventArgs e)
        {
            this.SetForegroundGradient();
        }

        private void OnForeColorEndChanged(object sender, DependencyPropertyChangedEventArgs e)
        {
            this.SetForegroundGradient();
        }

        private void OnForeColorStartChanged(object sender, DependencyPropertyChangedEventArgs e)
        {
            this.SetForegroundGradient();
        }

        private void OnQuantityCharactersChanged(object sender, DependencyPropertyChangedEventArgs args)
        {
            var quantity = 8;
            if (int.TryParse(QuantityCharacters, out quantity))
            {
                this.InitializeCaptcha(quantity);
            }
            else
            {
                throw new Exception("You have to set QuantityCharacter property");
            }
        }

        private void OnColorStartChanged(object sender, DependencyPropertyChangedEventArgs args)
        {
            this.SetBackgroundGradient();
        }

        private void OnColorEndChanged(object sender, DependencyPropertyChangedEventArgs args)
        {
            this.SetBackgroundGradient();
        }

        private void InitializeCaptcha(int quantityCharacters)
        {
            CaptchaLetters = new ObservableCollection<Captcha>();
            var captcha = new char[quantityCharacters];

            for (var x = 0; x < captcha.Length; x++)
            {
                captcha[x] = CharArray[random.Next(CharArray.Length)];
                var c = new Captcha
                            {
                                Letter = captcha[x].ToString(),
                                FontSize = random.Next(12, 72)
                            };
                SetTransformGroup(c);
                CaptchaLetters.Add(c);
            }

            var builder = new StringBuilder();
            foreach (var letter in CaptchaLetters)
            {
                builder.Append(letter.Letter);
            }
            CaptchaText = builder.ToString();
            items.ItemsSource = CaptchaLetters;
        }

        private void SetForegroundGradient()
        {
            if (IsUniqueForegroundColor)
            {
                foreach (var captcha in CaptchaLetters)
                {
                    captcha.ForegroundGradient = this.SetForegroundGradientSameColor();
                }
            }
            else
            {
                foreach (var captcha in CaptchaLetters)
                {
                    captcha.ForegroundGradient = this.SetForegroundGradientRandomColor();
                }
            }
        }

        private LinearGradientBrush SetForegroundGradientRandomColor()
        {
            var gradient = new LinearGradientBrush();
            gradient.StartPoint = new Point(0.5f, 1);
            gradient.EndPoint = new Point(0.8f, 0);
            gradient.GradientStops.Add(new GradientStop()
            {
                Color = Color.FromArgb((byte)random.Next(0, 255),
                    (byte)random.Next(0, 255),
                    (byte)random.Next(0, 255),
                    (byte)random.Next(0, 255)),
                Offset = 3
            });
            gradient.GradientStops.Add(new GradientStop()
            {
                Color = Color.FromArgb((byte)random.Next(0, 255),
                    (byte)random.Next(0, 255),
                    (byte)random.Next(0, 255),
                    (byte)random.Next(0, 255))
            });
            return gradient;
        }

        private LinearGradientBrush SetForegroundGradientSameColor()
        {
            var gradient = new LinearGradientBrush();
            gradient.StartPoint = new Point(0.5f, 1);
            gradient.EndPoint = new Point(0.8f, 0);
            gradient.GradientStops.Add(new GradientStop()
            {
                Color = ForegroundColorStart,
                Offset = 3
            });
            gradient.GradientStops.Add(new GradientStop()
            {
                Color = ForegroundColorEnd
            });
            return gradient;
        }

        private void SetBackgroundGradient()
        {
            var gradient = new LinearGradientBrush();
            gradient.StartPoint = new Point(0.5f, 0);
            gradient.EndPoint = new Point(0.5f, 1);

            if (IsUniqueBackgroundColor)
            {
                gradient.GradientStops.Add(new GradientStop() { Color = BackgroundColorStart, Offset = 3 });
                gradient.GradientStops.Add(new GradientStop() { Color = BackgroundColorStart });
            }
            else
            {
                gradient.GradientStops.Add(new GradientStop() { Color = BackgroundColorStart, Offset = 3 });
                gradient.GradientStops.Add(new GradientStop() { Color = BackgroundColorEnd });
            }
            
            contentLayout.Background = gradient;
        }

        private void SetTransformGroup(Captcha captcha)
        {
            this.SetRotate(ref captcha);
            this.SetScale(ref captcha);
            this.SetSkew(ref captcha);
            this.SetTranslate(ref captcha);
            this.SetFontColor(ref captcha);
        }

        private void SetRotate(ref Captcha captcha)
        {
            captcha.Rotate.Angle = random.Next(-10, 10);
            captcha.Rotate.CenterX = random.Next(1, 3);
            captcha.Rotate.CenterY = random.Next(1, 3);
            captcha.TransformGroup.Children.Add(captcha.Rotate);
        }

        private void SetScale(ref Captcha captcha)
        {
            captcha.Scale.ScaleX = random.Next(3, 5);
            captcha.Scale.ScaleY = random.Next(3, 4);
            captcha.Scale.CenterX = random.Next(1, 3);
            captcha.Scale.CenterY = random.Next(1, 3);
            captcha.TransformGroup.Children.Add(captcha.Scale);
        }

        private void SetSkew(ref Captcha captcha)
        {
            captcha.Skew.AngleX = random.Next(-10, 10);
            captcha.Skew.AngleY = random.Next(-10, 10);
            captcha.Skew.CenterX = random.Next(1, 3);
            captcha.Skew.CenterY = random.Next(1, 3);
            captcha.TransformGroup.Children.Add(captcha.Skew);
        }

        private void SetTranslate(ref Captcha captcha)
        {
            captcha.Translate.X = random.Next(2, 4);
            captcha.Translate.Y = random.Next(1, 4);
            captcha.TransformGroup.Children.Add(captcha.Translate);
        }

        private void SetFontColor(ref Captcha captcha)
        {
            captcha.FontColor = Color.FromArgb((byte)random.Next(0, 255),
                                               (byte)random.Next(0, 255),
                                               (byte)random.Next(0, 255),
                                               (byte)random.Next(0, 255));
        }
    }
}
